//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2003-2007
// Magellan and MapSend are registered trademarks of Thales Navigation.
//
// Magellan MapSend WPT waypoint/route parser.
//

# include "StdAfx.h"
# include "MapSendFile.h"
# include "Map.h"
# include "Types.h"
# include "Load.h"
# include "Log.h"
# include "Status.h"
# include "ParseHelpers.h"
# include "Wrappers.h"
# include "Globals.h"

class CMapSendWptLoader : public CMapLoader {
  public:
	// Override.
	virtual bool IsValid (const char * _pData, size_t _cFileSize) const;
	virtual LoadStatus_t Load (const char * _pData, size_t _cFileSize, const char * _strFileName, map_t * _pMap);
};

DECLARE_WPTRK_LOADER (CMapSendWptLoader, "mapsend-wpt", "wpt", "Magellan MapSend waypoints (*.wpt)", ftiWpts | ftiRoutes);

bool CMapSendWptLoader::IsValid (const char * _pData, size_t _cFileSize) const {
	if (_cFileSize < sizeof (CMapSendHdr))
		return false;

	const CMapSendHdr * const pHdr = reinterpret_cast<const CMapSendHdr *> (_pData);

	if (
		::strncmp (pHdr->strSignature, c_strMapSendSignature30, sizeof (pHdr->strSignature)) != 0 &&
		::strncmp (pHdr->strSignature, c_strMapSendSignature34, sizeof (pHdr->strSignature)) != 0 &&
		::strncmp (pHdr->strSignature, c_strMapSendSignature36, sizeof (pHdr->strSignature)) != 0
	)
		return false;
	if (pHdr->dwType != mstWaypointRoutes)
		return false;

	return true;
}

LoadStatus_t CMapSendWptLoader::Load (const char * _pData, size_t _cFileSize, const char * _strFileName, map_t * _pMap) {
	const char * const pEnd  = _pData + _cFileSize;

	file_refs_t file_refs;
	file_refs.push_back (file_ref_t ());
	file_refs.back ().strFileName = _strFileName;
	file_refs.back ().bAttachment = true;

	SetProgressStatus (0);

	//
	// Parse waypoints.
	//

	typedef std::map<string_t, rgn_t *> name2wpt_t;
	name2wpt_t name2wpt;

	const DWORD dwWaypoints = * reinterpret_cast<const DWORD *> (_pData + sizeof (CMapSendHdr));
	const char * pData = _pData + sizeof (CMapSendHdr) + sizeof (DWORD);

	for (size_t cWpt = 0; cWpt < dwWaypoints; ++ cWpt) {
		const char * const pWptBegin = pData;

		const BYTE btNameLen = * reinterpret_cast<const BYTE *> (pData);
		const char * const pName = pData + 1;

		const BYTE btCommentLen = * reinterpret_cast<const BYTE *> (pName + btNameLen); 
		const char * const pComment = pName + btNameLen + 1;

		const CMapSendWaypoint * const pWpt = reinterpret_cast<const CMapSendWaypoint *> (pComment + btCommentLen);
		pData = reinterpret_cast<const char *> (pWpt + 1);

		//
		// Create waypoint.
		//
		rgn_t & rgn = _pMap->CreateRgn (rkPoint);
		rgn.SetWaypointType ();

		rgn.pFileRef = & file_refs.back ();
		rgn.cBeginPos = pWptBegin - _pData;
		rgn.cEndPos = pEnd - _pData;
		rgn.cFileLine = -1;

		// Name.
		rgn.strLabel.assign (pName, btNameLen);

		// Description.
		if (btCommentLen)
			rgn.strComment.assign (pComment, btCommentLen);

		// Position.
		rgn.origins.push_back (rgn_origin_t ());
		rgn_origin_t & origin = rgn.origins.back ();
		origin.cLevel = 0;
		origin.cFileOffset = rgn.cBeginPos;
		origin.point.x =  pWpt->dblX;
		origin.point.y = -pWpt->dblY;

		// Extra.
		waypoint_info_t * const pWptExtra = rgn.pExtra->wtr.CreateWaypoint ();

		// Show.
		if (pWpt->btStatus == 0)
			pWptExtra->SetShow (waypoint_info_t::showHide);
		else
			pWptExtra->SetShow (waypoint_info_t::showName);

		// Altitude.
		pWptExtra->wFlags |= waypoint_info_t::defAltitude;
		pWptExtra->fAltitude = pWpt->dblAltitude;

		// Show.
		pWptExtra->SetShow (pWpt->btStatus == 0 ? waypoint_info_t::showHide : waypoint_info_t::showName);

		name2wpt [rgn.strLabel] = & rgn;

		SetProgressStatus (10*cWpt/dwWaypoints);
	}


	//
	// Parse routes.
	//

	const DWORD dwRoutes = * reinterpret_cast<const DWORD *> (pData);
	pData += sizeof (dwRoutes);

	for (size_t cRte = 0; cRte < dwRoutes; ++ cRte) {
		const char * const pRteBegin = pData;

		const BYTE btNameLen = * reinterpret_cast<const BYTE *> (pData);
		const char * const pName = pData + 1;

		const CMapSendRoute * const pRte = reinterpret_cast<const CMapSendRoute *> (pName + btNameLen);
		pData = reinterpret_cast<const char *> (pRte + 1);

		points_t points (pRte->dwWaypoints);
		routepoints_t rpoints (pRte->dwWaypoints);

		for (size_t cRpt = 0; cRpt < pRte->dwWaypoints; ++ cRpt) {
			const BYTE btWptNameLen = * reinterpret_cast<const BYTE *> (pData);
			const char * const pWptName = pData + 1;

			const CMapSendRoutePoint * const pRpt = reinterpret_cast<const CMapSendRoutePoint *> (pWptName + btWptNameLen);
			pData = reinterpret_cast<const char *> (pRpt + 1);

			point_t & point = points [cRpt];
			point.x =  pRpt->dblX;
			point.y = -pRpt->dblY;

			routepoint_info_t & rpoint = rpoints [cRpt];

			const string_t strWptName (pWptName, btWptNameLen);
			const name2wpt_t::iterator i = name2wpt.find (strWptName);
			if (i != name2wpt.end ()) {
				rgn_t * const pWpt = i->second;
				rpoint.pWaypoint = pWpt;
				assert (pWpt->origins [0].point == point);
			} else {
				assert (false);
				rpoint.pWaypoint = NULL;
			}
		}

		//
		// Create route.
		//
		rgn_t & rgn = _pMap->CreateRgn (rkPolyline);
		rgn.SetRouteType ();

		rgn.pFileRef  = & file_refs.back ();
		rgn.cBeginPos = pRteBegin - _pData;
		rgn.cEndPos   = pEnd - _pData;
		rgn.cFileLine = -1;

		rgn.strLabel.assign (pName, btNameLen);

		rgn.elements.push_back (rgn_element_t ());
		rgn_element_t & element = rgn.elements.back ();
		element.cFileOffset = rgn.cBeginPos;
		element.cLevel = 0;
		element.points.swap (points);

		route_info_t * const pRteExtra = rgn.pExtra->wtr.CreateRoute ();
		pRteExtra->points.swap (rpoints);

		SetProgressStatus (10 + 40*cRte/dwRoutes);
	}

	assert (pData == pEnd);

	_pMap->file_refs.swap (file_refs);

	// Suppress warning about empty Zoom etc.
	_pMap->bNoHeader = true;
	_pMap->cHeaderLen = sizeof (CMapSendHdr);

	SetProgressStatus (50);

	return lsOK;
}
